from cheqed.core.graph.side import TOP, BOTTOM
from cheqed.core.graph.traversal import preorder_edges, preorder_nodes

class Rule:
    def __init__(self, fn, ser):
        self.fn = fn
        self.ser = ser

    def __call__(self, *args, **kwargs):
        return self.fn(*args, **kwargs)

    def serialize(self):
        return self.ser

def goals(node):
    'Find all unmet subgoals of the given node.'
    goals = []
    for edge in preorder_edges(node.top):
        if not edge.is_connected:
            goals.append(edge)
    return goals

def sequents(node):
    if not hasattr(node, 'sequent'):
        if len(node.bottom) == 0:
            node.sequent = node.rule()
        elif len(node.bottom) == 1:
            node.sequent = node.rule(sequent(node.bottom[0].other))
        else:
            raise 'sequents may only be found for nodes with 0 or 1 bottom edges.'
    return node.sequent

def sequent(edge):
    'Find the sequent which maps to the given edge.'
    if edge.side != TOP:
        raise 'sequent may only be found for top edges.'
    return sequents(edge.node)[edge.index]

def extend(edge, node):
    edge.connect(node.bottom[0])
    try:
        [sequents(node) for node in preorder_nodes(node, TOP)
         if len(node.top) == 0 or not node.is_connected]
    except:
        edge.disconnect()
        raise

def serialize_edge(edge):
    if edge.is_connected:
        return serialize(edge.other.node)
    else:
        return 'noop()'

def serialize_container(container):
    return ', '.join([serialize_edge(edge) for edge in container])

def serialize_rule(rule):
    return rule.serialize()

def serialize_sequence(node):
    if len(node.top) == 1:
        if node.top.is_connected:
            return '%s, %s' % (serialize_rule(node.rule),
                               serialize_sequence(node.top[0].other.node))
        else:
            return serialize_rule(node.rule)
    else:
        return serialize(node)
        
def serialize(node):
    if len(node.top) == 0:
        return serialize_rule(node.rule)
    elif len(node.top) == 1:
        if node.top.is_connected:
            return 'sequence(%s)' % serialize_sequence(node)
        else:
            return serialize_rule(node.rule)
    else:
        return 'split(%s, %s)' % (serialize_rule(node.rule),
                                  serialize_container(node.top))
